/*************************************************************************
  Crytek Source File.
  Copyright (C), Crytek Studios, 2001-2010.
 -------------------------------------------------------------------------
  $Id$
  $DateTime$
  
 -------------------------------------------------------------------------
  History:
  - Nov 12th, 2010  : Created by Michelle Martin
 *************************************************************************/

#include "StdAfx.h"
#include "IKInteractionManager.h"
#include "IKSystem/IKTargetSystem.h"		// for starting/stopping IK Targets
#include "IActorSystem.h"								// To link the actor to the entity and stop him from moving
#include "IKSystem/IKTarget_AttachedHelperPos.h"  // for GetHelperPosFromEntityObject in the function ::GetIKHelperLocation
#include <IRenderAuxGeom.h>							// For DrawSphere and DrawLine
#include "IAnimatedCharacter.h"


const float REGRIP_ANGLE_2HWheel = DEG2RAD(75.0f);
const float REGRIP_ANGLE_1HWheel = DEG2RAD(45.0f);


CIKInteractionManager::CIKInteractionManager()
{
	m_systemActive = true;
	m_nextInteractionId = 1;
}

CIKInteractionManager::~CIKInteractionManager()
{
	// release everything in m_activeInteractions
	std::map<EntityId, TCharacterInteractions*>::const_iterator it = m_activeInteractions.begin();
	std::map<EntityId, TCharacterInteractions*>::const_iterator endIt = m_activeInteractions.end();
	for ( ; it != endIt; ++it)
	{
		TCharacterInteractions* pCharacterInteractions = it->second;
		pCharacterInteractions->clear();
		SAFE_DELETE(pCharacterInteractions);
	}
	m_activeInteractions.clear();
}


//////////////////////////////////////////////////////////////////////////

void CIKInteractionManager::Update(float frameTime)
{
#ifdef _DEBUG
	m_debugInfoRenderer.Render();
#endif // _DEBUG

	if (!m_systemActive)
		return;

	// don't do anything if there's nothing to do, obviously
	if (m_activeInteractions.empty())
		return;

	std::vector<std::map<EntityId, TCharacterInteractions*>::iterator> toBeDeleted;

	// Go through all characters that have interactions running
	std::map<EntityId, TCharacterInteractions*>::iterator it = m_activeInteractions.begin();
	std::map<EntityId, TCharacterInteractions*>::iterator endIt = m_activeInteractions.end();
	for ( ; it != endIt; ++it)
	{
		EntityId charEntityID = it->first;
		TCharacterInteractions* pCharacterInteractions = it->second;
		// Go through interactions for this character 
		int interactionCount = pCharacterInteractions->size();
		for (int i = 0; i < interactionCount; ++i)
		{
			// Performance NOTE:
			// Accessing vector by index - usually there will be only one active per character max, so this is not causing a performance hit, but allow for easy deletion
			SActiveInteraction& interaction = pCharacterInteractions->at(i);

			// reduce the timer until the next re-grip is allowed (when it reaches zero)
			if (interaction.m_regripAllowTimer > 0.0f)
				interaction.m_regripAllowTimer -= frameTime;
			else
				interaction.m_regripAllowTimer = 0.0f;
			
			// Update status and make callbacks to script, remove finished interactions
			switch (interaction.m_interactionState)
			{
			case IKState_InteractionAdded:
				{
					// Send messages to the IK Object and change the state to the next
					Script_OnInteractionTrigger(interaction.m_targetEntity, charEntityID, true);
					Script_CharacterIsFollowingObject(interaction.m_targetEntity, charEntityID, false);

					// default values if no animation must be started (or it fails)
					interaction.m_timerHelper = 0.0f;
					interaction.m_interactionState = IKState_StartIK;

					StartMainAnimations(charEntityID, interaction);

			
				}
				break;

			case IKState_WaitForAnimation:
				{
					// reduce the timer
					interaction.m_timerHelper -= frameTime;
					// if done, move on to the next state to start helper selection etc
					if (interaction.m_timerHelper <= 0.0f)
					{
						interaction.m_interactionState = IKState_StartIK;
						interaction.m_timerHelper = 0.0f;
					}
				}
				break;

			case IKState_StartIK:
				{
					// IK Handle Selection
					SelectIKHandles(*interaction.m_startParams);

					Vec3 helpVec = Vec3(0, 0, 0);
					TIKHelperList selectedHelpers;
					int errorCodes = IK_OK;
					errorCodes |= SelectAndCreateIKTargets(*interaction.m_startParams, selectedHelpers, false, helpVec);

					if (errorCodes != IK_OK)
					{
						// Stop the IK which might have been started in some rare cases
						IKFailed(errorCodes);
						StopIKInteraction(interaction.m_interactionId, 0.0f);
						interaction.m_interactionState = IKState_Finished;
						return;
					}
					interaction.m_selectedHelpers = selectedHelpers;
					interaction.m_helpVec = helpVec;

					// time to free up this unneeded memory
					SAFE_DELETE(interaction.m_startParams);

					interaction.m_interactionState = IKState_FadingIn;
				}
				break;

			case IKState_FadingIn:
				// Once fully blended in, send a message to the object and switch to the next state
				UpdateFadingIn(it->first, interaction);

				break;

			case IKState_Active:
				// detect the need for a regrip
				if (IsRegripNeeded(it->first, interaction))
					InitiateRegrip(it->first, interaction);
				break;

			case IKState_ReGrip_FadeOut:
				interaction.m_timerHelper -= frameTime;
				if (interaction.m_timerHelper <= 0.0f)
				{
					// --> after a good part of the fadeout is done, restart the process (select new targets and so on), switch to the fading in state
					// EntityId characterId, EntityId targetId, int flags, EIK_Object_Types objectType, const std::vector<uint64>& ikHandles
					SIKInteractionStartParameters params = CreateStartParamsFromInteraction(it->first, interaction);
					Vec3 helpVec = interaction.m_helpVec;
					SelectAndCreateIKTargets(params, interaction.m_selectedHelpers, true, helpVec, 0.35f);
					interaction.m_interactionState = IKState_FadingIn;
					interaction.m_regripAllowTimer = 0.8f;	// wait this long until the next re-grip is allowed (to prevent constant re-gripping)
					interaction.m_helpVec = helpVec;;
				}
				break;

			case IKState_FadingOut:
				// Check if all IK Targets are faded out completely yet
				UpdateFadingOut(it->first, interaction);
				break;

			case IKState_Finished:
				// Remove this interaction
				pCharacterInteractions->erase(pCharacterInteractions->begin() + i);

				// fix the loop variables after the erase
				--i;
				--interactionCount;

				// Make a callback to the IK Object that the interaction completely done
				Script_OnInteractionTrigger(interaction.m_targetEntity, it->first, false);
				break;

			default:
				CRY_ASSERT_MESSAGE(false, "IK Interaction: Unknown Interaction status found - please adjust code.");
				CryWarning(VALIDATOR_MODULE_GAME, VALIDATOR_ERROR, "IK Interaction: Unknown IK Interaction State - please fix the code.");
				break;
			}  // END of switch statement

		} // END of loop over interactions for this character

		// if character has no active interactions (as the past update might have removed them): Note it for removal
		if (pCharacterInteractions->empty())
			toBeDeleted.push_back(it);

	} // END of loop over all characters with active interactions in the maps

	// Remove characters with finished interactions
	std::vector<std::map<EntityId, TCharacterInteractions*>::iterator>::iterator deletionIt = toBeDeleted.begin();
	std::vector<std::map<EntityId, TCharacterInteractions*>::iterator>::iterator deletionItEnd = toBeDeleted.end();
	for ( ; deletionIt != deletionItEnd; ++deletionIt)
	{
		// Remove any AnimationGraph state listeners that might have been added
		IAnimationGraphState *pGraphState = NULL;
		IActor* pActor = gEnv->pGame->GetIGameFramework()->GetIActorSystem()->GetActor((*deletionIt)->first);
		if (pActor)
		{
			IAnimatedCharacter* pAnimChar = pActor->GetAnimatedCharacter();
			if (pAnimChar)
			{
				pGraphState = pAnimChar->GetAnimationGraphState();
				pGraphState->RemoveListener(this);
			}
		}

		// erase the character from the list
		m_activeInteractions.erase(*deletionIt);
	}

	toBeDeleted.clear();
}

//////////////////////////////////////////////////////////////////////////

void CIKInteractionManager::UpdateFadingOut( EntityId entity, SActiveInteraction& activeInteraction )
{
	// Check if all IK Targets are faded out completely yet
	bool stillActive = false;
	std::vector<uint64>::const_iterator ikLimbit = activeInteraction.m_ikHandles.begin();
	std::vector<uint64>::const_iterator ikTLimbEndIt = activeInteraction.m_ikHandles.end();
	for ( ; ikLimbit != ikTLimbEndIt; ++ikLimbit)
		stillActive |= CCryAction::GetCryAction()->GetIKTargetSystem()->ExistsIKTargetForCharacterLimb(entity, *ikLimbit);

	if (stillActive)
		return;

	// Set state to finished, so the next update will remove it completely
	activeInteraction.m_interactionState = IKState_Finished;

}

//////////////////////////////////////////////////////////////////////////

void CIKInteractionManager::UpdateFadingIn( EntityId entity, SActiveInteraction& activeInteraction )
{
	// Once all limbs are fully blended in, send a message to the object and switch to the next state

	// Check how many IK Targets are faded in completely already
	int fadedInCount = 0;
	std::vector<uint64>::const_iterator ikLimbit = activeInteraction.m_ikHandles.begin();
	std::vector<uint64>::const_iterator ikTLimbEndIt = activeInteraction.m_ikHandles.end();
	for ( ; ikLimbit != ikTLimbEndIt; ++ikLimbit)
	{
		if (CCryAction::GetCryAction()->GetIKTargetSystem()->IsIKTargetForCharacterLimbFullyBlendedIn(entity, *ikLimbit))
			++fadedInCount;
	}

	if (fadedInCount == activeInteraction.m_ikHandles.size())
	{
		// If all limbs are blended in, change state
		activeInteraction.m_interactionState = IKState_Active;

		// and send a message to the IK object
		Script_CharacterIsFollowingObject(activeInteraction.m_targetEntity, entity, true);

		// POSSIBLE IMPROVEMENT: Special Handling depending on object type, flags or params how many IK Limbs must be faded in completely as minimum 
		//       before the animations can be started. (this is when the callback can already be triggered)
	}
}

//////////////////////////////////////////////////////////////////////////

void CIKInteractionManager::StopAllIKInteractions()
{
	// go through all m_activeInteractions
	std::map<EntityId, TCharacterInteractions*>::const_iterator itInteractions = m_activeInteractions.begin();
	std::map<EntityId, TCharacterInteractions*>::const_iterator itEndInteractions = m_activeInteractions.end();
	TCharacterInteractions::iterator itCharActions;
	TCharacterInteractions::const_iterator itEndCharActions;
	for ( ; itInteractions != itEndInteractions; ++itInteractions)
	{
		// go through all interactions for this character
		TCharacterInteractions* pCharInteractions = itInteractions->second;
		itCharActions = pCharInteractions->begin();
		itEndCharActions = pCharInteractions->end();
		for ( ; itCharActions != itEndCharActions; ++itCharActions)
		{
			SActiveInteraction* activeInteraction = &(*itCharActions);
			EntityId charEntityID = itInteractions->first;

			StopInteraction(activeInteraction, charEntityID, 0.0f);
		}
	}
}

//////////////////////////////////////////////////////////////////////////

bool CIKInteractionManager::StopIKInteraction( int interactionId, float blendOutTime /* = 0.7f */ )
{
	// go through all m_activeInteractions and find an interaction with that id
	std::map<EntityId, TCharacterInteractions*>::const_iterator itInteractions = m_activeInteractions.begin();
	std::map<EntityId, TCharacterInteractions*>::const_iterator itEndInteractions = m_activeInteractions.end();
	TCharacterInteractions::iterator itCharActions;
	TCharacterInteractions::const_iterator itEndCharActions;
	for ( ; itInteractions != itEndInteractions; ++itInteractions)
	{
		// go through all interactions for this character and check if one of them is the one
		TCharacterInteractions* pCharInteractions = itInteractions->second;
		itCharActions = pCharInteractions->begin();
		itEndCharActions = pCharInteractions->end();
		for ( ; itCharActions != itEndCharActions; ++itCharActions)
		{
			SActiveInteraction* activeInteraction = &(*itCharActions);
			if (activeInteraction->m_interactionId == interactionId)
			{
				// Found! STOP this interaction		
				EntityId charEntityID = itInteractions->first;
				StopInteraction(activeInteraction, charEntityID, blendOutTime);
				return true;
			}
		}
	}

	return false;
}

//////////////////////////////////////////////////////////////////////////

void CIKInteractionManager::StopIKInteraction( EntityId charId, EntityId objectID, float blendOutTime /*= 0.7f*/ )
{
	// go through all m_activeInteractions and find an interaction with that id
	std::map<EntityId, TCharacterInteractions*>::const_iterator itInteractions = m_activeInteractions.begin();
	std::map<EntityId, TCharacterInteractions*>::const_iterator itEndInteractions = m_activeInteractions.end();
	TCharacterInteractions::iterator itCharActions;
	TCharacterInteractions::const_iterator itEndCharActions;
	for ( ; itInteractions != itEndInteractions; ++itInteractions)
	{
		EntityId charEntityID = itInteractions->first;
		if (charEntityID != charId)
			continue;

		// go through all interactions for this character and check if one of them is the one
		TCharacterInteractions* pCharInteractions = itInteractions->second;
		itCharActions = pCharInteractions->begin();
		itEndCharActions = pCharInteractions->end();
		for ( ; itCharActions != itEndCharActions; ++itCharActions)
		{
			SActiveInteraction* activeInteraction = &(*itCharActions);
			if (activeInteraction->m_targetEntity == objectID)
				StopInteraction(activeInteraction, charEntityID, blendOutTime);
		}

		return;
	}

}

//////////////////////////////////////////////////////////////////////////

void CIKInteractionManager::StopInteraction( SActiveInteraction* activeInteraction, EntityId charEntityID, float blendOutTime )
{
	// Stop all IK Targets
	std::vector<uint64>::const_iterator ikIterator = activeInteraction->m_ikHandles.begin();
	for ( ; ikIterator != activeInteraction->m_ikHandles.end(); ++ikIterator)
	{
		CCryAction::GetCryAction()->GetIKTargetSystem()->StopIKonCharacterLimb(charEntityID, *ikIterator, blendOutTime);
	}

	// Mark this interaction for fading out or finished, depending on blendout time
	activeInteraction->m_interactionState = (blendOutTime > 0.0f) ? IKState_FadingOut : IKState_Finished;

	// Notify the object about the fading out of the IK
	Script_CharacterIsFollowingObject(activeInteraction->m_targetEntity, charEntityID, false);

	// Un-Pause the AnimationGraph if this is needed
	if (activeInteraction->m_graphPaused)
	{
		IAnimationGraphState *pGraphState = NULL;
		IActor* pActor = gEnv->pGame->GetIGameFramework()->GetIActorSystem()->GetActor(charEntityID);
		if (pActor)
		{
			IAnimatedCharacter* pAnimChar = pActor->GetAnimatedCharacter();
			if (pAnimChar)
			{
				pGraphState = pAnimChar->GetAnimationGraphState();
				pGraphState->Pause(false, eAGP_ObjectInteraction);
			}
		}

	} 
	else if (activeInteraction->m_graphInputID != -1)
	{   // Restore AnimationGraph input value if needed
		
		IAnimationGraphState *pGraphState = NULL;
		IActor* pActor = gEnv->pGame->GetIGameFramework()->GetIActorSystem()->GetActor(charEntityID);
		if (pActor) 
		{
			IAnimatedCharacter* pAnimChar = pActor->GetAnimatedCharacter();
			if (pAnimChar)
			{
				pGraphState = pAnimChar->GetAnimationGraphState();
				pGraphState->SetInput(activeInteraction->m_graphInputID, activeInteraction->m_graphPrevValue.c_str());
			}
		}
	} // END OF AnimationGraph restoring (if..else)

}

//////////////////////////////////////////////////////////////////////////

int CIKInteractionManager::StartNewIKInteraction( const SIKInteractionStartParameters& params )
{
	if (!m_systemActive)
	{
		return IKFailed(IKErr_System_Is_Disabled);
	}

	int errorCodes = IK_OK;

	// The actual parameters will start out as a copy of the params that are passed in,
	// but they might be modified in this process
	SIKInteractionStartParameters newParams = params;

	// Valid Parameters Check
	errorCodes |= CheckParameters(newParams);
	if (errorCodes != IK_OK)
		return IKFailed(errorCodes);

	// POSSIBLE IMPROVEMENT: If this is an actor, request whether he is allowed to do IK or not (he might be linked to a vehicle or entity etc.
	// Make this some kind of callback to the game - this way the IK can be prevented without access to this source code by modifying the game DLL


	Vec3 helpVec = Vec3(0, 0, 0);

	// Store in active Interactions vector
	// Point of no return

	// POSSIBLE IMPROVEMENT: Extract the lines below to a separate method for code cleanliness
	TCharacterInteractions* pCharacterInteractions = GetActiveInteractionsForCharacter(newParams.m_characterId);
	if (pCharacterInteractions == NULL)
	{
		pCharacterInteractions = new TCharacterInteractions();
		m_activeInteractions[newParams.m_characterId] = pCharacterInteractions;
	}
	SActiveInteraction newInteraction(m_nextInteractionId, newParams);
//	newInteraction.m_selectedHelpers = selectedHelpers;
	newInteraction.m_helpVec = helpVec;
	pCharacterInteractions->push_back(newInteraction);



	// POSSIBLE IMPROVEMENT: LookAt IK (if flags are set)
	// --> Special handling depending on object type (where to look)

	++m_nextInteractionId;
	return newInteraction.m_interactionId;
}

//////////////////////////////////////////////////////////////////////////

int CIKInteractionManager::CheckParameters( const SIKInteractionStartParameters& params )
{
	int retVal = 0;

	// --> Target Entity ID valid?
	IEntity* pTargetEntity = gEnv->pEntitySystem->GetEntity(params.m_targetId);
	if (!pTargetEntity)
		retVal |= IKErr_No_Valid_TargetEntityID;

	// --> Character Entity ID valid?
	IEntity* pCharacterEntity = gEnv->pEntitySystem->GetEntity(params.m_characterId);
	if (!pCharacterEntity)
		return IKErr_No_Valid_CharacterEntityID;

	// --> Does Character have the right interface?
	ICharacterInstance* pCharacterInstance = pCharacterEntity->GetCharacter(0);
	if (!pCharacterInstance)
		retVal |= IKErr_No_Valid_CharacterInterface;

	return retVal;
}

//////////////////////////////////////////////////////////////////////////

int CIKInteractionManager::SelectIKHandles( SIKInteractionStartParameters& params )
{
	// Select one or more IK Handles to use


	// --> any IK Handles specified?
	int handleCount = params.m_ikHandles.size();
	if (handleCount == 0)
		return IKErr_No_Valid_IK_Handles;


	// --> Do (all) the specified IK Handles exist?
	ICharacterInstance* pCharacterInstance = gEnv->pEntitySystem->GetEntity(params.m_characterId)->GetCharacter(0);
	for (int i = 0; i < handleCount; ++i)
	{
		// does IK exist?
		if (-1 == pCharacterInstance->GetICharacterModel()->GetICharacterModelSkeleton()->GetLimbDefinitionIdx(params.m_ikHandles.at(i)))
		{
			--handleCount;
			if (handleCount > 0)  // at least one left
			{
				// --> Fallback: Use only existing one, if there is at least one
				CryLog("IK Interaction: Invalid IK Handle specified. Will remove entry and use remaining provided handles.");
				params.m_ikHandles.erase(params.m_ikHandles.begin() + i);
				--i;
				continue;
			}
			else
				return IKErr_No_Valid_IK_Handles;
		}

	}


	// POSSIBLE IMPROVEMENT --> Special handling depending on flags 
	// (for example select one handle from the list, first one available, or first free one etc...)
	// if an IK Handle is already busy (part of another interaction), either end that interaction, remove that limb from it 
	//			 or remove this limb from this interaction (depending on flags)


	// --> Special handling depending on object type
	// (for example minimum amount of valid handles, e.g. 2-handed valves etc...)


	return IK_OK;
}

//////////////////////////////////////////////////////////////////////////


int CIKInteractionManager::EnumerateIKHelpersOnObject( const SIKInteractionStartParameters& params, TIKHelperList& ikHelperList )
{
	ikHelperList.clear();

	string helperPrefix = "IKTarget";

	// --> Special Handling: get prefix for this character if he has one defined (different character types might use different prefixes)

	// Enumerate attachment targets
	EnumerateIKHelpers_Attachment(params, ikHelperList, helperPrefix);

	// Enumerate helper targets (exported with the cgf)
	EnumerateIKHelpers_Helper(params, ikHelperList, helperPrefix);

	// Enumerate bone targets (bone names)
	EnumerateIKHelpers_Bone(params, ikHelperList, helperPrefix);


	// Special handling for object type
	// --> for example additional prefixes, call the enumerate functions again with a different one
	
	// Special handling for character setup
	// --> for example additional or exclusive prefixes, call the enumerate functions again with a different one


	// If no helpers were found, fallback to entity position
	if (ikHelperList.empty())
	{
		IKHelper entityPos(	gEnv->pEntitySystem->GetEntity(params.m_targetId)->GetName(), IKHelper_EntityPos) ;
		ikHelperList.push_back(entityPos);

		// --> print out warning message about this
		CryWarning(VALIDATOR_MODULE_GAME, VALIDATOR_WARNING, "IK Interaction: No markup of any kind found on the target entity (bone, helpers or attachment). Falling back to using entity position instead.");
	}

	return IK_OK;
}

//////////////////////////////////////////////////////////////////////////

void CIKInteractionManager::EnumerateIKHelpers_Attachment( const SIKInteractionStartParameters& params, TIKHelperList& ikHelperList, string& helperPrefix )
{
	// --> Does the target object even have a character instance?
	IEntity* targetEntity = gEnv->pEntitySystem->GetEntity(params.m_targetId);
	ICharacterInstance* pCharacterInstance = targetEntity->GetCharacter(0);
	if (pCharacterInstance == NULL)
		return;

	// --> Does the target object have an attachment manager?
	IAttachmentManager* pAttachmentManager = pCharacterInstance->GetIAttachmentManager();
	if (pAttachmentManager == NULL)
		return;

	// --> Does the target object have any attachments?
	int attachmentCount = pAttachmentManager->GetAttachmentCount();
	if (attachmentCount == 0)
		return;


	// go through all character attachments
	// retrieve their name and compare the prefix
	// and add all found ones to the list
	for (int i = 0; i < attachmentCount; ++i)
	{
		IAttachment* attachment = pAttachmentManager->GetInterfaceByIndex(i);
		if (NameHasPrefix(attachment->GetName(), helperPrefix))
		{
			IKHelper newHelper(attachment->GetName(), IKHelper_Attachment);
			ikHelperList.push_back(newHelper);
		}
	}

}

//////////////////////////////////////////////////////////////////////////

void CIKInteractionManager::EnumerateIKHelpers_Helper( const SIKInteractionStartParameters& params, TIKHelperList& ikHelperList, string& helperPrefix )
{
	IEntity* targetEntity = gEnv->pEntitySystem->GetEntity(params.m_targetId);

	// --> go through all entity slots
	int slotCount = targetEntity->GetSlotCount();
	for (int i = 0; i < slotCount; i++)
	{
		// --> check if there is a static object in this slot
		IStatObj *pObj = targetEntity->GetStatObj(i);
		if (!pObj)
			continue;

		// --> Go through this object's exported helpers
		if (pObj->GetGeoName() != NULL)
		{
			_smart_ptr<IStatObj> pRootObj = gEnv->p3DEngine->LoadStatObj(pObj->GetFilePath());
			if (pRootObj)
			{
				int subObjectCount = pRootObj->GetSubObjectCount();
				for (int j = 0; j < subObjectCount; ++j)
				{
					IStatObj::SSubObject* pSubObj = pRootObj->GetSubObject(j);
					if (NameHasPrefix(pSubObj->name, helperPrefix))
					{
						IKHelper newHelper(pSubObj->name, IKHelper_Helper);
						ikHelperList.push_back(newHelper);
					}
				} // for loop over all sub objects
			} // if (pRootObj)
		} // if (pObj->GetGeoName() != NULL)
	}

}

//////////////////////////////////////////////////////////////////////////


void CIKInteractionManager::EnumerateIKHelpers_Bone( const SIKInteractionStartParameters& params, TIKHelperList& ikHelperList, string& helperPrefix )
{
	// --> Does the target object even have a character with bones in slot 0?
	IEntity* targetEntity = gEnv->pEntitySystem->GetEntity(params.m_targetId);
	ICharacterInstance* pCharacterInstance = targetEntity->GetCharacter(0);
	if (pCharacterInstance == NULL)
		return;
	ISkeletonPose* pSkeletonPose = pCharacterInstance->GetISkeletonPose();
	if (pSkeletonPose == NULL)
		return;

	// --> does the skeleton have any bones?
	int boneCount = pSkeletonPose->GetJointCount();
	if (boneCount == 0)
		return;

	// go through all character bones
	// retrieve their name and compare the prefix
	// and add all found ones to the list
	for (int i = 0; i < boneCount; ++i)
	{
		if (NameHasPrefix(pSkeletonPose->GetJointNameByID(i), helperPrefix))
		{
			IKHelper newHelper(pSkeletonPose->GetJointNameByID(i), IKHelper_Bone);
			ikHelperList.push_back(newHelper);
		}
	}
}

//////////////////////////////////////////////////////////////////////////

bool CIKInteractionManager::NameHasPrefix( const char* name, string& prefix )
{
	string strName(name);
	if (strName.find(prefix) == 0)
		return true;
	return false;
}

//////////////////////////////////////////////////////////////////////////

const QuatT CIKInteractionManager::GetIKHelperLocation( EntityId entityID, const IKHelper& ikHelper ) const
{
	IEntity* pTargetEntity = gEnv->pEntitySystem->GetEntity(entityID);
	switch (ikHelper.m_helperType)
	{
	case IKHelper_Helper:
		{
			return CIKTarget_AttachedHelperPos::GetHelperPosFromEntityObject(pTargetEntity, ikHelper.m_name);
		}
		break;

	case IKHelper_Bone:
		{
			ISkeletonPose*			pSkeletonPose = pTargetEntity->GetCharacter(0)->GetISkeletonPose();
			return QuatT(pTargetEntity->GetSlotWorldTM(0)) *  pSkeletonPose->GetAbsJointByID(pSkeletonPose->GetJointIDByName(ikHelper.m_name));
		}
		break;

	case IKHelper_Attachment:
		{
			return pTargetEntity->GetCharacter(0)->GetIAttachmentManager()->GetInterfaceByName(ikHelper.m_name)->GetAttWorldAbsolute();
		}
		break;

	case IKHelper_EntityPos:
		{
			return QuatT(pTargetEntity->GetWorldTM());
		}
		break;

	default:
		CRY_ASSERT_MESSAGE(0, "Object Interactions: Unknown IK Helper Type encountered. IK could look wrong as a result.");
		return QuatT(pTargetEntity->GetWorldTM());
		break;
	}

}

//////////////////////////////////////////////////////////////////////////

Vec3 CIKInteractionManager::CalculateWorldLocationsOfIKHelpers( TIKSelectionHelperList &selectionHelperList, TIKHelperList &ikHelperList, EntityId targetId )
{
	Vec3 middlePoint(ZERO);
	selectionHelperList.resize(ikHelperList.size());
	TIKHelperList::const_iterator it = ikHelperList.begin();
	TIKHelperList::const_iterator endIt = ikHelperList.end();
	for (int i = 0 ; it != endIt; ++it, ++i)
	{
		QuatT tempLoc = GetIKHelperLocation(targetId, (*it));;
		selectionHelperList[i].worldLocation = tempLoc;
		middlePoint += tempLoc.t;
		selectionHelperList[i].helperIndex = i;
		selectionHelperList[i].inUseByLimb = false;
	}
	middlePoint = middlePoint * (1.0f / ikHelperList.size());
	return middlePoint;
}

//////////////////////////////////////////////////////////////////////////

void CIKInteractionManager::CalculateIKHandleRootLocations( TIKLimbRootPositions &limbRootPositions, SIKInteractionStartParameters &params )
{
	limbRootPositions.resize(params.m_ikHandles.size());
	std::vector<uint64>::const_iterator it = params.m_ikHandles.begin();
	std::vector<uint64>::const_iterator endIt = params.m_ikHandles.end();
	for (int i = 0; it != endIt; ++it, ++i)
	{
		limbRootPositions[i].worldLocation = CIKTarget_Base::GetLimbRootWorldPos(params.m_characterId, (*it));
	}
}

//////////////////////////////////////////////////////////////////////////

int CIKInteractionManager::ChooseIKHelperForLimbs( SIKInteractionStartParameters& params, TIKHelperList& ikHelperList, TIKHelperList& ignoreSelectedHelpers, Vec3 &helpVec )
{
	// POSSIBLE IMPROVEMENT: Reachability Test for each helper

	//! Chooses an appropriate helper for each IK Limb that will be part of the interaction from the enumerated list.
	//! If there are less helpers than limbs the function will remove the additional limbs from the interaction.

	// --> Special handling depending on object type

	switch (params.m_objectType)
	{
	case EIKObject_TwoHandedWheel:
		{
			// pick exactly two IK Helpers for exactly two IK Limbs
			int ikError = ChooseIKHelpers_TwoHandedWheel(params, ikHelperList, ignoreSelectedHelpers, helpVec);
			if (ikError != IK_OK)
				return ikError;
		}
		break;

	case EIKObject_OneHandedWheel:	// one-handed wheels only have one helper
	case EIKObject_Button:			// buttons only have one helper
	case EIKObject_Default: 
	default: 
		{
			// Just choose the first helper for each limb

			// Remove Selected Helpers if this is desired (for re-grip for example)
			if (!ignoreSelectedHelpers.empty() && ikHelperList.size() > ignoreSelectedHelpers.size())
			{
				// Remove helpers
				for (int i = 0; i < ignoreSelectedHelpers.size(); ++i)
				{
					TIKHelperList::iterator it = ikHelperList.begin();
					for ( ; it != ikHelperList.end(); ++it)
					{
						if ((*it) == ignoreSelectedHelpers.at(i))
						{
							ikHelperList.erase(it);
							break;
						}
					}
				}
			}

			// just pick the first n helpers to match the n limbs

			// more helpers than limbs
			int limbCount = params.m_ikHandles.size();
			while (ikHelperList.size() > limbCount)
				ikHelperList.pop_back();

			// more limbs than helpers
			int helperCount = ikHelperList.size();
			while (params.m_ikHandles.size() > helperCount)
				params.m_ikHandles.pop_back();
		}
		break;
	}



	if (params.m_ikHandles.size() == 0 || ikHelperList.size() == 0)
		return IKErr_No_Helper_Selection_Possible;

	return IK_OK;
}

//////////////////////////////////////////////////////////////////////////

int CIKInteractionManager::CreateIKTargetsForAllLimbs( SIKInteractionStartParameters& params, const TIKHelperList& ikHelperList, float fBlendTime /*= 0.55f*/ )
{
	CRY_ASSERT_MESSAGE(params.m_ikHandles.size() == ikHelperList.size(), "IK Interaction: Error, the number of limbs and IK targets do not match! Will most likely crash, please fix the code.");

	float blendTime = fBlendTime;

	// get the name of the spine IK handle (if one was declared in the IK Target setup for this character)
	IEntity* pCharacterEntity = gEnv->pEntitySystem->GetEntity(params.m_characterId);
	ICharacterInstance* pCharacterInstance = pCharacterEntity->GetCharacter(0);
	const SIKTargetCharData* charIKTargetData = pCharacterInstance->GetICharacterModel()->GetICharacterModelSkeleton()->GetCharIKTargetData();
	uint64 spineIKHandle = charIKTargetData->m_spineHandleHash;

	// Go through all limbs and add an IK Target for it
	std::vector<uint64>::const_iterator itIKHandle = params.m_ikHandles.begin();
	TIKHelperList::const_iterator itIKHelper = ikHelperList.begin();
	for (int i = 0; itIKHandle != params.m_ikHandles.end(); ++itIKHandle, ++itIKHelper)
	{
		SIKTargetParams ikParams(params.m_characterId, *itIKHandle, true, blendTime, itIKHelper->m_offset, itIKHelper->m_offsetIsGlobal, spineIKHandle);
		if (params.m_limbAnimations.find(*itIKHandle) != params.m_limbAnimations.end())
		{
			ikParams.limbAnimName = params.m_limbAnimations[*itIKHandle];
			std::map<uint64, int16>::const_iterator itLayer = charIKTargetData->m_animLayers.find(*itIKHandle);
			if (charIKTargetData->m_animLayers.end() != itLayer)
			{
				ikParams.limbAnimLayer = itLayer->second;
			}
			else
			{
				ikParams.limbAnimLayer = 10 + i;
				++i;
			}
		}
		
		int result;
		switch (itIKHelper->m_helperType)
		{
		case IKHelper_EntityPos:
			result = CCryAction::GetCryAction()->GetIKTargetSystem()->AddIKTarget_EntityPos(ikParams, params.m_targetId);
			break;
		case IKHelper_Helper:
			result = CCryAction::GetCryAction()->GetIKTargetSystem()->AddIKTarget_AttachedHelperPos(ikParams, params.m_targetId, itIKHelper->m_name);
			break;
		case IKHelper_Bone:
			result = CCryAction::GetCryAction()->GetIKTargetSystem()->AddIKTarget_Bone(ikParams, params.m_targetId, itIKHelper->m_name);
			break;
		case IKHelper_Attachment:
			result = CCryAction::GetCryAction()->GetIKTargetSystem()->AddIKTarget_Attachment(ikParams, params.m_targetId, itIKHelper->m_name);
			break;
		default:
			return IKErr_Unknown_Helper_Type;
		}

		if (result != 0)
			CIKTargetSystem::WriteErrorMessage(result);
	}

	return IK_OK;
}

//////////////////////////////////////////////////////////////////////////

CIKInteractionManager::TCharacterInteractions* CIKInteractionManager::GetActiveInteractionsForCharacter( EntityId entityID )
{
	std::map<EntityId, TCharacterInteractions*>::iterator it;
	it = m_activeInteractions.find(entityID);
	if (it == m_activeInteractions.end())
		return NULL;

	return it->second;
}

//////////////////////////////////////////////////////////////////////////

ScriptAnyValue CIKInteractionManager::EntityIdToScript( EntityId id )
{
	if (id)
	{
		ScriptHandle hdl;
		hdl.n = id;
		return ScriptAnyValue(hdl);
	}
	else
	{
		return ScriptAnyValue();
	}
}

//////////////////////////////////////////////////////////////////////////

int CIKInteractionManager::IKFailed( int errorCodes )
{
	// Mis-Use of this function Check
	if (errorCodes == IK_OK)
	{
		CryWarning(VALIDATOR_MODULE_GAME, VALIDATOR_WARNING, "IK Interaction: No known errors occurred, but system is aborting.");
		return false;
	}

	if (errorCodes & IKErr_No_Valid_CharacterEntityID)
		CryWarning(VALIDATOR_MODULE_GAME, VALIDATOR_ERROR, "IK Interaction: Could not find a valid character entity, aborting.");
	if (errorCodes & IKErr_No_Valid_CharacterInterface)
		CryWarning(VALIDATOR_MODULE_GAME, VALIDATOR_ERROR, "IK Interaction: Character has no ICharacterInstance interface, aborting.");
	if (errorCodes & IKErr_No_Valid_TargetEntityID)
		CryWarning(VALIDATOR_MODULE_GAME, VALIDATOR_ERROR, "IK Interaction: Could not find a valid target entity, aborting.");
	if (errorCodes & IKErr_No_Valid_IK_Handles)
		CryWarning(VALIDATOR_MODULE_GAME, VALIDATOR_ERROR, "IK Interaction: No or no valid IK Handles specified, aborting.");
	if (errorCodes & IKErr_No_Helper_Selection_Possible)
		CryWarning(VALIDATOR_MODULE_GAME, VALIDATOR_ERROR, "IK Interaction: Impossible to choose any good IK Helpers for the chosen IK Handles, aborting.");
	if (errorCodes & IKErr_Unknown_Helper_Type)
		CryWarning(VALIDATOR_MODULE_GAME, VALIDATOR_ERROR, "IK Interaction: Unknown Helper type found - please fix the code, aborting.");
	if (errorCodes & IKErr_Need_Two_Limbs)
		CryWarning(VALIDATOR_MODULE_GAME, VALIDATOR_ERROR, "IK Interaction: Need at least two IK Limbs for this object type, aborting. Maybe try selecting a different Object Type in the properties?");
	if (errorCodes & IKErr_System_Is_Disabled)
		CryWarning(VALIDATOR_MODULE_GAME, VALIDATOR_ERROR, "IK Interaction: The interaction system is disabled and new interactions cannot be started.");
	

	return -1;
}

//////////////////////////////////////////////////////////////////////////

void CIKInteractionManager::Script_OnInteractionTrigger( EntityId targetEntityId, EntityId character, bool isStarting )
{
	IEntity* targetEntity = gEnv->pEntitySystem->GetEntity(targetEntityId);
	if (!targetEntity)
	{
		CryWarning(VALIDATOR_MODULE_GAME, VALIDATOR_ERROR, "IK Interaction: Target Entity with id %i no longer exists. Entity removed before IK was stopped?", targetEntityId);
	}

	SmartScriptTable table = targetEntity->GetScriptTable();
	if (table->HaveValue("OnInteractionTrigger"))
		Script::CallMethod( table, "OnInteractionTrigger", EntityIdToScript(character), isStarting );
}

//////////////////////////////////////////////////////////////////////////

void CIKInteractionManager::Script_CharacterIsFollowingObject( EntityId targetEntityId, EntityId character, bool ikIsBlendedIn )
{
	IEntity* targetEntity = gEnv->pEntitySystem->GetEntity(targetEntityId);
	if (!targetEntity)
	{
		CryWarning(VALIDATOR_MODULE_GAME, VALIDATOR_ERROR, "IK Interaction: Target Entity with id %i no longer exists. Entity removed before IK was stopped?", targetEntityId);
		return;
	}

	SmartScriptTable table = targetEntity->GetScriptTable();
	if (table->HaveValue("CharacterIsFollowingObject"))
		Script::CallMethod( table, "CharacterIsFollowingObject", EntityIdToScript(character), ikIsBlendedIn );
}

//////////////////////////////////////////////////////////////////////////

int CIKInteractionManager::ChooseIKHelpers_TwoHandedWheel( SIKInteractionStartParameters &params, TIKHelperList &ikHelperList, TIKHelperList& ignoreSelectedHelpers, Vec3& OT1OT2 )
{
	// Safety Check
	// Remove additional limbs if there are not enough helpers (there can only be two)
	while (params.m_ikHandles.size() > 2 || params.m_ikHandles.size() > ikHelperList.size())
		params.m_ikHandles.pop_back();

	// Safety Check - are there enough limbs?
	if (params.m_ikHandles.size() < 2)
		return IKErr_Need_Two_Limbs;

	// make a list with all the helpers positions, indices and a bool marking them as inUse
	TIKSelectionHelperList selectionHelperList;
	Vec3 helperMiddle = CalculateWorldLocationsOfIKHelpers(selectionHelperList, ikHelperList, params.m_targetId);

	// get all the root's positions
	TIKLimbRootPositions limbRootPositions;
	CalculateIKHandleRootLocations(limbRootPositions, params);

	// calculate the middle between all roots
	Vec3 midPoint = (limbRootPositions[0].worldLocation.t + limbRootPositions[1].worldLocation.t) * 0.5f;
	Vec3 R1R2 = (limbRootPositions[1].worldLocation.t - limbRootPositions[0].worldLocation.t).GetNormalized();

	// move the mid point more to the height of the helpers to get better results
	Vec3 RootToMid = (gEnv->pEntitySystem->GetEntity(params.m_characterId)->GetWorldPos() - midPoint).GetNormalized();

	bool useRegripSelection = !OT1OT2.IsZero();

	// just swap targets if thre are only two helpers
	if (useRegripSelection && ikHelperList.size() == 2)
	{
		IKHelper target1 = ignoreSelectedHelpers.at(1);
		IKHelper target2 = ignoreSelectedHelpers.at(0);
		ikHelperList.clear();
		ikHelperList.push_back(target1);
		ikHelperList.push_back(target2);

		return IK_OK;
	}



	// used for re-gripping only
	float regripAnglePrev = OT1OT2 * R1R2;
	float bestRegripAngle = 1.0f; // angle between the two tested helpers and the root line (same test as for regrip)
//	if (!ignoreSelectedHelpers.empty())
//		OT1OT2 = (selectionHelperList.at(ignoreSelectedHelpers.at(1)).worldLocation.t - selectionHelperList.at(ignoreSelectedHelpers.at(0)).worldLocation.t).GetNormalized();

	// find the two targets with the biggest opening angle
	TIKSelectionHelperList::iterator ikHelperIt1 = selectionHelperList.begin();
	TIKSelectionHelperList::const_iterator endIt = selectionHelperList.end();
	short bestHelper1 = 0;
	short bestHelper2 = 0;
	
	float bestAngle = useRegripSelection ? 2.0f : -1.0f;
	float bestOverallDist = -1.0f;
	for ( ; ikHelperIt1 != endIt; ++ikHelperIt1)
	{
		// The helpers already selected should not be available for the same limb again (to avoid re-gripping to the same helper).
		// Note: the list of selected helpers will have been cleared if this is not desired previously by the calling function.
		if (!ignoreSelectedHelpers.empty() && ikHelperList.at(ikHelperIt1->helperIndex) == ignoreSelectedHelpers.at(0))
			continue;

		float bestTargetDist = -1.0f;
		TIKSelectionHelperList::iterator ikHelperIt2 = selectionHelperList.begin();
		for ( ; ikHelperIt2 != endIt; ++ikHelperIt2)
		{
			// don't select the same helper for both hands (already pretty unlikely, but this way we safe calculations)
			if (ikHelperIt1 == ikHelperIt2)
				continue;

			// The helpers already selected should not be available for the same limb again (to avoid re-gripping to the same helper).
			// Note: the list of selected helpers will have been cleared if this is not desired previously by the calling function.
			if (!ignoreSelectedHelpers.empty() && ikHelperList.at(ikHelperIt2->helperIndex) == ignoreSelectedHelpers.at(1))
				continue;

			// The vector from the first helper to the second helper (normalized) - used for angle calculations
			Vec3 T1T2 = (selectionHelperList.at(ikHelperIt2->helperIndex).worldLocation.t - selectionHelperList.at(ikHelperIt1->helperIndex).worldLocation.t).GetNormalized();

			// eliminate combinations where the arms are crossed over
			// and don't pick a pair if it would immediately trigger a re-grip
			float cosAngle = T1T2 * R1R2;
			if (/*cosAngle < cosf(REGRIP_ANGLE) ||*/ cosAngle < 0) // arms are crossed if cosAngle is negative, or close to this angle (do NOT remove the 0 check, REGRIP_ANGLE is unknown and user-set)
			{
				continue;
			}


			IEntity* pCharacterEntity = gEnv->pEntitySystem->GetEntity(params.m_characterId);
			ICharacterInstance* pCharacterInstance = pCharacterEntity->GetCharacter(0);
			const SIKTargetCharData* charIKTargetData = pCharacterInstance->GetICharacterModel()->GetICharacterModelSkeleton()->GetCharIKTargetData();

			// Maximum distance between the helpers for further natural look improvement
			// Make this value actually dependent on the arm length (approx 1.3 arm lengths)
			float maxDist = charIKTargetData->m_armLengthDefault * 1.3f; //.65f; 
			float targetDist = (ikHelperIt1->worldLocation.t - ikHelperIt2->worldLocation.t).GetLength();
			if (targetDist > maxDist)
			{
				//CryLogAlways("Combination target %i and %i discarded - distance: %.3f degrees.", ikHelperIt1->helperIndex, ikHelperIt2->helperIndex, targetDist);
				continue;
			}


			// SECOND LEVEL CHECK - at this point we have a second IK Helper that is the maximum aways from the selected first one

			if (useRegripSelection)
			{
				// find the one pair that is approx the opposite of the current selection
				// about the same angle to R1R2 and about 90 degrees or more to OT1OT2

				cosAngle = T1T2 * OT1OT2;

				// slightly different algortihm for smaller and larger wheels
				if (ikHelperList.size() <= 4)
				{
					if (fabsf(cosAngle) < (fabsf(bestAngle) + 0.05f)) // within a certain range of the best angle so far (closest to 90 degrees)
					{
						// factors to consider: 
						// distance between targets
						// angle of the vector between the two targets in comparison to the old two targets (find the best "opposite")

						// find the angle that sort of mirrors the current vector between the two targets best (looks the most natural)
						float gripAngle = T1T2 * R1R2;

						// find an angle close to 90 degrees to the original one
						if ((fabsf(regripAnglePrev) - fabsf(gripAngle)) > (fabsf(regripAnglePrev) - fabsf(bestRegripAngle) + 0.06f))
							continue;

						// If the angle is better and the overall distance is also bigger... do not continue obviously
						// but if the distance is smaller than the overall distance and it's not the biggest distance within this helper either...
						if (targetDist < bestTargetDist && targetDist < bestOverallDist)
							continue;

						// If the angle isn't smaller and the distance isn't either, continue
						if (cosAngle >= bestAngle && targetDist < bestOverallDist)
							continue;

						// If the angle isn't a nice opposite and the distance isn't even bigger, continue
						if (( (fabsf(regripAnglePrev) - fabsf(gripAngle)) > (fabsf(regripAnglePrev) - fabsf(bestRegripAngle)) ) && targetDist < bestOverallDist)
							continue;

						bestOverallDist = targetDist;
						bestTargetDist = targetDist;

						if ((fabsf(regripAnglePrev) - fabsf(gripAngle)) < (fabsf(regripAnglePrev) - fabsf(bestRegripAngle)))
							bestRegripAngle = gripAngle; // only update if actually smaller, to avoid error accumulation

						if (cosAngle < bestAngle)
							bestAngle = cosAngle; // only update if actually smaller, to avoid error accumulation

						bestHelper1 = ikHelperIt1->helperIndex;
						bestHelper2 = ikHelperIt2->helperIndex;
					}
				}
				else
				{
					if (cosAngle < (bestAngle + 0.05f/* + cosf(DEG2RAD(25.0f))*/)) // within a certain range of the best angle so far
					{
						// factors to consider: 
						// distance between targets
						// angle of the vector between the two targets in comparison to the old two targets (find the best "opposite")

						// find the angle that sort of mirrors the current vector between the two targets best (looks the most natural)
						float gripAngle = T1T2 * R1R2;
						if (fabsf(gripAngle) > (fabsf(bestRegripAngle) + 0.06f))
							continue;

						// If the angle is better and the overall distance is also bigger... do not continue obviously
						// but if the distance is smaller than the overall distance and it's not the biggest distance within this helper either...
						if (targetDist < bestTargetDist && targetDist < bestOverallDist)
							continue;

						// If the angle isn't smaller and the distance isn't either, continue
						if (cosAngle >= bestAngle && targetDist < bestOverallDist)
							continue;

						if (fabsf(gripAngle) > fabsf(bestRegripAngle) && targetDist < bestOverallDist)
							continue;

						bestOverallDist = targetDist;
						bestTargetDist = targetDist;

						if (fabsf(gripAngle) < fabsf(bestRegripAngle))
							bestRegripAngle = gripAngle; // only update if actually smaller, to avoid error accumulation

						if (cosAngle < bestAngle)
							bestAngle = cosAngle; // only update if actually smaller, to avoid error accumulation

						bestHelper1 = ikHelperIt1->helperIndex;
						bestHelper2 = ikHelperIt2->helperIndex;
					}
				}

			}
			else
			{
				if (targetDist < bestTargetDist)
					continue;
				bestTargetDist = targetDist;

				// Calculate the opening angle for these two
				Vec3 M_Helper1_norm = (ikHelperIt1->worldLocation.t - midPoint);
				Vec3 M_Helper2_norm = (ikHelperIt2->worldLocation.t - midPoint);
				// Make them flat in xy-area of the character
				M_Helper1_norm = (M_Helper1_norm - RootToMid * (M_Helper1_norm * RootToMid) * 0.5f).GetNormalized();
				M_Helper2_norm = (M_Helper2_norm - RootToMid * (M_Helper2_norm * RootToMid) * 0.5f).GetNormalized();
				float alpha = acosf(M_Helper1_norm * M_Helper2_norm);

				//CryLogAlways("Combination target %i and %i - angle: %.3f degrees.", ikHelperIt1->helperIndex, ikHelperIt2->helperIndex, alpha * 180.0f / 3.14f);

				// Is it better than the already existing one?
				if (alpha > bestAngle)
				{
					bestAngle = alpha;
					bestHelper1 = ikHelperIt1->helperIndex;
					bestHelper2 = ikHelperIt2->helperIndex;
				}
				else if (alpha > bestAngle * 0.85f)
				{
					// might still be a better option if this set is considerably closer to the body and within a reasonable angle
					float bestCompoundDist = (selectionHelperList.at(bestHelper1).worldLocation.t - midPoint).GetLength() + (selectionHelperList.at(bestHelper2).worldLocation.t - midPoint).GetLength();
					float currentCompoundDist = (ikHelperIt1->worldLocation.t - midPoint).GetLength() + (ikHelperIt2->worldLocation.t - midPoint).GetLength();
					if (currentCompoundDist < bestCompoundDist * 0.95f)
					{
						bestHelper1 = ikHelperIt1->helperIndex;
						bestHelper2 = ikHelperIt2->helperIndex;
						// don't update bestAngle though, to avoid compounding an error
					}
				}
			}
		}
	}

	// Swap targets if necessary, to avoid the arms being crossed in front
	Vec3 T1T2 = (selectionHelperList.at(bestHelper2).worldLocation.t - selectionHelperList.at(bestHelper1).worldLocation.t).GetNormalized();
	float cosAngle = T1T2 * R1R2;

	// swap if cosAngle is negative
	if (cosAngle < 0)
	{
		short temp = bestHelper1;
		bestHelper1 = bestHelper2;
		bestHelper2 = temp;
		//CryLogAlways("Swapped targets");
	}

	// put everything in the final list
	IKHelper target1 = ikHelperList[bestHelper1];
	IKHelper target2 = ikHelperList[bestHelper2];
	ikHelperList.clear();
	ikHelperList.push_back(target1);
	ikHelperList.push_back(target2);

	return IK_OK;
}


//////////////////////////////////////////////////////////////////////////

int CIKInteractionManager::CalculateOffsetsForIKHelpers( SIKInteractionStartParameters& params, TIKHelperList& ikHelperList, Vec3 &helpVec )
{
	// --> Special handling depending on object type
	// --> Special handling depending on character setup

	// Get the character IK Target setup
	IEntity* pCharacterEntity = gEnv->pEntitySystem->GetEntity(params.m_characterId);
	ICharacterInstance* pCharacterInstance = pCharacterEntity->GetCharacter(0);
	const SIKTargetCharData* charIKTargetData = pCharacterInstance->GetICharacterModel()->GetICharacterModelSkeleton()->GetCharIKTargetData();
	if (charIKTargetData == NULL)
		charIKTargetData = new SIKTargetCharData();


	// Go through all helpers
	TIKHelperList::iterator it = ikHelperList.begin();
	TIKHelperList::iterator endIt = ikHelperList.end();
	for (int idx = 0; it != endIt; ++it, ++idx)
	{
		// get the pullback amount for this limb (either default value or a limb specific value) --> IK file sets this up
		Vec3 limbPullBack = charIKTargetData->m_endEffPullbackDefault;
		std::map<uint64, Vec3>::const_iterator pullBackIt = charIKTargetData->m_endEffPullback.find(params.m_ikHandles[idx]);
		if (pullBackIt != charIKTargetData->m_endEffPullback.end())
			limbPullBack = pullBackIt->second;
	

		IKHelper& ikHelper = (*it);
		// some rotations need to be flipped if they are left of the center of the character
		Vec3 R = CIKTarget_Base::GetLimbRootWorldPos(params.m_characterId, params.m_ikHandles[idx]).t;
		Vec3 C = gEnv->pEntitySystem->GetEntity(params.m_characterId)->GetWorldPos();
		Vec3 fwdDir = gEnv->pEntitySystem->GetEntity(params.m_characterId)->GetForwardDir();
		Vec3 upDir = gEnv->pEntitySystem->GetEntity(params.m_characterId)->GetWorldRotation() * Vec3(0,0,1);
		float xAngleModifier = ((((R-C) % fwdDir).GetNormalized() * upDir) < 0.0f) ? -1.0f : 1.0f;

		// SPECIAL: adjustment based on type
		//////////////////////////////////////////////////////////////////////////
		switch (params.m_objectType)
		{
		case EIKObject_WheelHandle:
			{
				// calculate a global offset
				ikHelper.m_offsetIsGlobal = true;

				// get local z axis
				QuatT targetLoc = GetIKHelperLocation(params.m_targetId, ikHelper);
				Vec3 targetZAxis = targetLoc.q * Vec3(0,0,1);
				Vec3 targetXAxis = targetLoc.q * Vec3(1,0,0);

				// get root to target vector
				Vec3 rootToTarget = (targetLoc.t - R).GetNormalized();

				// make it flat --> this is the x axis to use
				Vec3 yAxis = rootToTarget % targetZAxis.GetNormalized();
				Vec3 xAxis = (targetZAxis % (yAxis)).GetNormalized(); //(rootToTarget - targetZAxis * (rootToTarget * targetZAxis)).GetNormalized();

				// get the original xAxis and the calculated one and set the difference on top of the target rotation
				float zAngle = acosf(targetXAxis * xAxis);
				if (yAxis * targetXAxis < 0.0f)
					zAngle *= -1.0f;
				ikHelper.m_offset.q = targetLoc.q* Quat::CreateRotationZ(zAngle); // * (Quat::CreateRotationV0V1(targetXAxis, xAxis));// * (Quat::CreateRotationY(yAngle));

				// there can be cases where it'll look better if the hand was rotated 180 around the long axis
				float tempMod = 1.0f;
				if (upDir * targetZAxis < 0.0f)
				{
					ikHelper.m_offset.q = ikHelper.m_offset.q * Quat::CreateRotationX(DEG2RAD(180.0f) * xAngleModifier);
					tempMod = -1.0f; // because for a turned hand, some rotations need to be in the opposite direction
				}

				// adjust a little for more natural look
				ikHelper.m_offset.q = ikHelper.m_offset.q * Quat::CreateRotationZ(-DEG2RAD(25.0f) * tempMod);
				ikHelper.m_offset.q = ikHelper.m_offset.q * Quat::CreateRotationX(DEG2RAD(-10.0f) * xAngleModifier);

				// additional rotation
				ikHelper.m_offset.q = ikHelper.m_offset.q * params.m_additionalOffsetRot;

				// offset everything a few centimeters into x and y
				ikHelper.m_offset.t = ikHelper.m_offset.q * (limbPullBack + params.m_additionalOffsetPos);
			}
			break;

		case EIKObject_Button:
			{
				QuatT targetLoc = GetIKHelperLocation(params.m_targetId, ikHelper);
				Vec3 targetYAxis = targetLoc.q * Vec3(0,1,0);
				Vec3 targetXAxis = targetLoc.q * Vec3(1,0,0);

				// find a good up vector for the hand (x axis, along the fingers

				// get up axis from character (world) and get forward axis from character (world)
				// add them to get a diagonal forward/upward pointing vector
				Vec3 diagonalFwd = (upDir + fwdDir).GetNormalized();
				// cross y with diagonal 
				Vec3 temp = targetYAxis % diagonalFwd;
				// cross result with y
				Vec3 xAxis = (temp % targetYAxis).GetNormalized();
				// Result is the x vector for the hand
				helpVec = xAxis;

				// get the original xAxis and the calculated one and set the difference on top of the target rotation
				float zAngle = acosf(xAxis * targetXAxis);
				if (((targetXAxis % xAxis) * targetYAxis) < 0)  // rotation direction needs to be fixed
					zAngle *= -1.0f;
				ikHelper.m_offset.q.SetRotationY(zAngle);


				// apply additional offset
				// go out the original y axis a few cm and down towards the palm
				// dependent on character setup 
				ikHelper.m_offset.q = ikHelper.m_offset.q * params.m_additionalOffsetRot;
				ikHelper.m_offset.t  = limbPullBack + (ikHelper.m_offset.q * (params.m_additionalOffsetPos + Vec3(-0.04f, 0, 0))); 

			}
			break;

		case EIKObject_Lever:
			{
				
				// get an ergonomic rotation, but non-global
				// get local z axis
				QuatT targetLoc = GetIKHelperLocation(params.m_targetId, ikHelper);
				Vec3 targetZAxis = targetLoc.q * Vec3(0,0,1);
				Vec3 targetXAxis = targetLoc.q * Vec3(1,0,0);
				
				// get root to target vector
				Vec3 root = (R * 2.0f + C) * (1.0f / 3.0f);
				Vec3 rootToTarget = targetLoc.t - root;
				
				// make it flat --> this is the x axis to use
				Vec3 xAxis = (rootToTarget - targetZAxis * (rootToTarget * targetZAxis) + Vec3(0.01f, 0.01f, 0.01f)).GetNormalized();
				
				// get the original xAxis and the calculated one and set the difference on top of the target rotation
				float zAngle = acosf(xAxis * targetXAxis); // * 0.95f - DEG2RAD(30.0f);
				ikHelper.m_offset.q.SetRotationZ(zAngle);
				ikHelper.m_offset.q = ikHelper.m_offset.q * Quat::CreateRotationY(-DEG2RAD(20.0f) * xAngleModifier);

				// additional rotation
				ikHelper.m_offset.q = ikHelper.m_offset.q * params.m_additionalOffsetRot;

				ikHelper.m_offset.t  = (ikHelper.m_offset.q * (limbPullBack + params.m_additionalOffsetPos)); 
			}
			break;

		case EIKObject_TwoHandedWheel:
			{
				// Move back for the fingers
				// Move out for the palm
				ikHelper.m_offset.t.x = limbPullBack.x;
				ikHelper.m_offset.t.y = limbPullBack.y;
				ikHelper.m_offset.t.z = limbPullBack.z + ((-0.03f) * xAngleModifier);
				ikHelper.m_offset.t += params.m_additionalOffsetPos;
				// z angle is determined by the distance of the two targets from each other
				QuatT targetLoc1 = GetIKHelperLocation(params.m_targetId, ikHelperList.at(0));
				QuatT targetLoc2 = GetIKHelperLocation(params.m_targetId, ikHelperList.at(1));
				float distVal = min(max((targetLoc1.t - targetLoc2.t).GetLength() - 0.15f, 0.0f), 0.6f) / 0.6f;
				float angle = (distVal * 40.0f) - 20.0f;

				ikHelper.m_offset.q = Quat::CreateRotationZ(-DEG2RAD(angle)) * Quat::CreateRotationY(-DEG2RAD(25.0f) * xAngleModifier) * Quat::CreateRotationX(-DEG2RAD(20.0f) * xAngleModifier);
				// additional rotation
				ikHelper.m_offset.q = ikHelper.m_offset.q * params.m_additionalOffsetRot;
			}
			break;

		case EIKObject_OneHandedWheel:
			{
				ikHelper.m_offset.t.zero();
				ikHelper.m_offset.t.y = limbPullBack.y;
				ikHelper.m_offset.t += params.m_additionalOffsetPos;

				// the fingers of the hand should point away from the body (and the shoulder/root)
				// Get target exact location
				QuatT targetLoc = GetIKHelperLocation(params.m_targetId, ikHelper);

				// Get vector from root to target
				Vec3 rootToTarget = (fwdDir + upDir).GetNormalized(); // targetLoc.t - R;

				// Get the helpers x and y axes
				Vec3 targetXAxis = targetLoc.q * Vec3(1,0,0);
				Vec3 targetYAxis = targetLoc.q * Vec3(0,1,0);
				targetXAxis.NormalizeSafe();
				targetYAxis.NormalizeSafe();

				// Multiply y axis of helper with that vector and subtract y axis times that scalar from the vector (to make it flat)
				rootToTarget = rootToTarget - (targetYAxis * (rootToTarget * targetYAxis));

				// normalize that vector
				rootToTarget.NormalizeSafe();

				// multiply x axis of helper with the vector and take the acos
				float alpha = acosf(targetXAxis * rootToTarget);
				// set offset to rotate around the y axis by this amount
				//ikHelper.m_offset.q = Quat::CreateRotationY(alpha) * Quat::CreateRotationZ(DEG2RAD(25.0f));
				ikHelper.m_offset.q = Quat::CreateRotationY(alpha);
				if (targetYAxis * (targetXAxis % rootToTarget) < 0)
					ikHelper.m_offset.q = Quat::CreateRotationY(-alpha);

				// additional rotation
				ikHelper.m_offset.q = ikHelper.m_offset.q * Quat::CreateRotationZ(DEG2RAD(25.0f)) * params.m_additionalOffsetRot;
				ikHelper.m_offset.t += ikHelper.m_offset.q * Vec3(limbPullBack.x,0,limbPullBack.z);

				// store the hand vector in world coordinates for this, it helps with the detection for re-grip conditions later
				helpVec = targetLoc.q * ikHelper.m_offset.q * Vec3(1.0f,0,0);

/*
#if _DEBUG
				m_debugInfoRenderer.sphere1 = targetLoc.t;
				m_debugInfoRenderer.pos1 = targetLoc.t;
				m_debugInfoRenderer.dir1 = targetLoc.q * ikHelper.m_offset.q * Vec3(1.0f, 0, 0);;
				m_debugInfoRenderer.pos2 = targetLoc.t;
				m_debugInfoRenderer.dir2 = targetLoc.q * ikHelper.m_offset.q * Vec3(0, 1.0f, 0);
				m_debugInfoRenderer.pos3 = targetLoc.t;
				m_debugInfoRenderer.dir3 = targetLoc.q * ikHelper.m_offset.q * Vec3(0, 0, 1.0f);;
#endif
*/
			}
			break;

		default:
			
			// GENERIC handling
			//////////////////////////////

			// Move back for the fingers
			// Move out for the palm 
			ikHelper.m_offset.t = limbPullBack;

			// Apply additional offset
			ikHelper.m_offset.t += params.m_additionalOffsetPos;

			// Rotate a bit for a more relaxed wrist pose
			ikHelper.m_offset.q.SetRotationZ(DEG2RAD(17.0f));

			// additional rotation
			ikHelper.m_offset.q = ikHelper.m_offset.q * params.m_additionalOffsetRot;
			break;
		}
	}

	return IK_OK;

}

//////////////////////////////////////////////////////////////////////////

bool CIKInteractionManager::IsRegripNeeded( EntityId entity, SActiveInteraction& activeInteraction )
{
	// suppress all re-grips for this interaction? (return false right away in this case)
	if (activeInteraction.m_flags & EIAC_NOREGRIP)
		return false;
	
	// POSSIBLE IMPROVEMENT --> has a helper become unreachable?

	// If a re-grip was just executed wait for at least xx seconds before another one can be triggered
	if (activeInteraction.m_regripAllowTimer > 0.0f)
		return false;

	// depending on object type
	// --> arms crossing (two-handed wheels)
	// --> hand turned too far	(one-handed wheels)
	switch(activeInteraction.m_objectType)
	{

	case EIKObject_TwoHandedWheel:
		{
			// Do IK Targets still exist?
			if (NULL == ((CIKTargetSystem*)CCryAction::GetCryAction()->GetIKTargetSystem())->GetIKTargetForCharacterLimb(entity, activeInteraction.m_ikHandles[0]))
				return false;
			if (NULL == ((CIKTargetSystem*)CCryAction::GetCryAction()->GetIKTargetSystem())->GetIKTargetForCharacterLimb(entity, activeInteraction.m_ikHandles[1]))
				return false;

			// detect possible arms crossing (two-handed wheels)
			Vec3 R1 = CIKTarget_Base::GetLimbRootWorldPos(entity, activeInteraction.m_ikHandles[0]).t;
			Vec3 R2 = CIKTarget_Base::GetLimbRootWorldPos(entity, activeInteraction.m_ikHandles[1]).t;
			Vec3 T1 = ((CIKTargetSystem*)CCryAction::GetCryAction()->GetIKTargetSystem())->GetIKTargetForCharacterLimb(entity, activeInteraction.m_ikHandles[0])->GetCurrentTargetWorlPos().t;
			Vec3 T2 = ((CIKTargetSystem*)CCryAction::GetCryAction()->GetIKTargetSystem())->GetIKTargetForCharacterLimb(entity, activeInteraction.m_ikHandles[1])->GetCurrentTargetWorlPos().t;
			Vec3 R1R2 = (R2 - R1);
			Vec3 T1T2 = (T2 - T1);

			// arms are crossed if cosAngle is negative
			// POSSIBLE IMPROVEMENT: Make the angle at which this triggers adjustable 
			R1R2.Normalize();
			T1T2.Normalize();
			float cosAngle = T1T2 * R1R2;
			if (cosAngle < cosf(REGRIP_ANGLE_2HWheel))
			{
				activeInteraction.m_helpVec = T1T2;
				return true;
			}
		}
		break;

	case EIKObject_OneHandedWheel:
		// A Re-Grip is needed if the hand is turned more than REGRIP_ANGLE_1HWheel degrees in either direction to the forearm
		{
			// Do IK Targets still exist?
			if (NULL == ((CIKTargetSystem*)CCryAction::GetCryAction()->GetIKTargetSystem())->GetIKTargetForCharacterLimb(entity, activeInteraction.m_ikHandles[0]))
				return false;

			// get the current angle of the hand/end-effector 
			QuatT currentLimbLoc = CIKTarget_Base::GetLimbEndeffectorWorldPos(entity, activeInteraction.m_ikHandles[0]);
			Vec3 currentVec = currentLimbLoc.q * Vec3(1.0f, 0, 0);
			
			// activeInteraction.m_helpVec stores the starting vector from the current grip
			// Measure the angle difference
			float cosAngle = currentVec * activeInteraction.m_helpVec;
			// is it bigger than REGRIP_ANGLE_1HWheel? (==> is the cosine smaller?)
			if (cosAngle < cosf(REGRIP_ANGLE_1HWheel))
			{
				activeInteraction.m_helpVec = currentVec; // store current hand vec for finding a good position later
				return true;
			}
			break;
		}
		break;

	default:
		// all other types do not support re-gripping
		break;
	}

	return false;
}

//////////////////////////////////////////////////////////////////////////

void CIKInteractionManager::InitiateRegrip( EntityId entity, SActiveInteraction& activeInteraction )
{
	// --> if re-grip necessary then send signal, stop the ik targets, goto state regrip_fadeout
	// --> after a certain amount of the fadeout, restart the process (select new targets and so on), switch to the fading in state
	float blendOutTimer = 1.3f;

	// POSSIBLE IMPROVEMENT: Depending on type and flags, maybe just let one limb go (or all _but_ one)

	// Notify the object about the fading out of the IK
	Script_CharacterIsFollowingObject(activeInteraction.m_targetEntity, entity, false);



	// Stop all IK Targets
	std::vector<uint64>::const_iterator ikIterator = activeInteraction.m_ikHandles.begin();
	for ( ; ikIterator != activeInteraction.m_ikHandles.end(); ++ikIterator)
	{
		CCryAction::GetCryAction()->GetIKTargetSystem()->StopIKonCharacterLimb(entity, *ikIterator, blendOutTimer);
	}

	// Mark this interaction for fading out
	activeInteraction.m_interactionState = IKState_ReGrip_FadeOut;
	activeInteraction.m_timerHelper = blendOutTimer * 0.12f;	// After this amount of the fadeout it will be stopped (and blended to the new helpers)

}

//////////////////////////////////////////////////////////////////////////

int CIKInteractionManager::SelectAndCreateIKTargets( SIKInteractionStartParameters& params, TIKHelperList& selectedHelpers, bool ignoreSelectedHelpers, Vec3& helpVec, float fBlendTime /*= 0.55f*/ )
{
	int errorCodes = IK_OK;

	// Enumerate IK Helper Markup (all supported kinds, by name)
	TIKHelperList ikHelperList;
	errorCodes |= EnumerateIKHelpersOnObject(params, ikHelperList);

	if (!ignoreSelectedHelpers)
		selectedHelpers.clear();


	// Select an IK Helper for each IK Handle
	errorCodes |= ChooseIKHelperForLimbs(params, ikHelperList, selectedHelpers, helpVec);


	if (errorCodes != IK_OK)
		return errorCodes;


	// Offset Calculation
	errorCodes |= CalculateOffsetsForIKHelpers(params, ikHelperList, helpVec);


	// IKTarget creation
	errorCodes |= CreateIKTargetsForAllLimbs(params, ikHelperList, fBlendTime);

	selectedHelpers = ikHelperList;

	return errorCodes;
}

//////////////////////////////////////////////////////////////////////////

SIKInteractionStartParameters CIKInteractionManager::CreateStartParamsFromInteraction( EntityId entity, const SActiveInteraction& activeInteraction )
{
	SIKInteractionStartParameters params;
	params.Reset();
	params.m_characterId		= entity;
	params.m_targetId			= activeInteraction.m_targetEntity;
	params.m_flags				= activeInteraction.m_flags;
	params.m_objectType			= activeInteraction.m_objectType;
	params.m_ikHandles			= activeInteraction.m_ikHandles;
	params.m_additionalOffsetPos = activeInteraction.m_additionalOffsetPos;
	params.m_additionalOffsetRot = activeInteraction.m_additionalOffsetRot;
	params.m_limbAnimations		= activeInteraction.m_limbAnimations;
	return params;
}

//////////////////////////////////////////////////////////////////////////

void CIKInteractionManager::QueryComplete( TAnimationGraphQueryID queryID, bool succeeded )
{
	// Go through all interactions that have an outstanding query for the graph:

	// Go through all characters that have interactions running
	std::map<EntityId, TCharacterInteractions*>::iterator it = m_activeInteractions.begin();
	std::map<EntityId, TCharacterInteractions*>::iterator endIt = m_activeInteractions.end();
	for ( ; it != endIt; ++it)
	{
		TCharacterInteractions* pCharacterInteractions = it->second;
		// Go through interactions for this character 
		int interactionCount = pCharacterInteractions->size();
		for (int i = 0; i < interactionCount; ++i)
		{
			// Performance NOTE:
			// Accessing vector by index - usually there will be only one active per character max, so this is not causing a performance hit, but allow for easy deletion
			SActiveInteraction& interaction = pCharacterInteractions->at(i);
			if (interaction.m_interactionState == IKState_WaitForAnimation)
			{
				if (!interaction.m_startParams)
					continue;

				if (interaction.m_startParams->m_graphQueryID == queryID)
				{
					// If one is found that matches that query: set the state to StartIK so helpers are selected and the IK is triggered
					interaction.m_interactionState = IKState_StartIK;
					return;
				}
			}		
		}
	}
}

//////////////////////////////////////////////////////////////////////////

int CIKInteractionManager::IsValidInteraction( int interactionId )
{
	//! Returns 0 if the interaction is not existing or in the finished state
	//! Returns 1 if the interaction is active
	//! Returns 2 if the interaction is active but currently fading out (for finishing, not regripping)

	EntityId dummy;
	SActiveInteraction* activeInteraction = GetInteractionById(interactionId, dummy);
	if (!activeInteraction)
		return 0;

	if (activeInteraction->m_interactionState == IKState_FadingOut)
		return 2;
	else
		return 1;

	return 0;
}

//////////////////////////////////////////////////////////////////////////

void CIKInteractionManager::EnableSystem( bool enable )
{
	if (enable)
	{
		m_systemActive = true;
	}
	else
	{
		StopAllIKInteractions();

		// Update once more to remove everything finally
		Update(0.001f);
		m_systemActive = false;
	}
}

//////////////////////////////////////////////////////////////////////////

void CIKInteractionManager::StartMainAnimations( EntityId charEntityID, SActiveInteraction &interaction )
{
	// Start optional animations and/or set AnimationGraph inputs if needed (start timer too)
	// get graph interface
	IAnimationGraphState *pGraphState = NULL;
	IActor* pActor = gEnv->pGame->GetIGameFramework()->GetIActorSystem()->GetActor(charEntityID);
	if (pActor)
	{
		IAnimatedCharacter* pAnimChar = pActor->GetAnimatedCharacter();
		if (pAnimChar)
			pGraphState = pAnimChar->GetAnimationGraphState();
	}

	// Option 1: Start animation directly
	if (!interaction.m_startParams->m_animName.empty())
	{
		IEntity* pEntity = gEnv->pEntitySystem->GetEntity(charEntityID);
		ICharacterInstance* pCharacterInstance = pEntity->GetCharacter(0);
		if (pCharacterInstance != NULL)
		{
			ISkeletonAnim* pSkeletonAnim = pCharacterInstance->GetISkeletonAnim();
			if (pSkeletonAnim != NULL)
			{
				// prepare animation parameters
				CryCharAnimationParams AParams;
				AParams.m_nLayerID				= interaction.m_startParams->m_animLayer;
				AParams.m_fPlaybackSpeed		= 1.0f;
				AParams.m_fTransTime			= interaction.m_startParams->m_animBlendTime;	
				AParams.m_fAllowMultilayerAnim	= 1;
				// forcing skeleton update because the characters pose is relevant for the helper selection
				AParams.m_nFlags |= CA_FORCE_SKELETON_UPDATE | CA_FULL_ROOT_PRIORITY; 
				AParams.m_nFlags |= CA_LOOP_ANIMATION;
				bool bAnimStarted = pSkeletonAnim->StartAnimation(interaction.m_startParams->m_animName.c_str(), AParams);

				// wait until the transition is completely done before continuing with the IK stuff
				interaction.m_timerHelper = interaction.m_startParams->m_animBlendTime;
				interaction.m_interactionState = IKState_WaitForAnimation;

				// only pause the graph if the animation was successfully started
				if (interaction.m_startParams->m_animPausesGraph && bAnimStarted)
					if (pGraphState)
					{
						// if a graph exists, pause it
						pGraphState->Pause(true, eAGP_ObjectInteraction);
						interaction.m_graphPaused = true;
					}
			}
		}
	}


	// Option 2: Start the AG input
	if (pGraphState && !interaction.m_startParams->m_graphInputName.empty())
	{
		// don't set an input if the graph was paused
		if (!interaction.m_graphPaused)
		{
			// get the inputs current value
			int graphInputID = pGraphState->GetInputId(interaction.m_startParams->m_graphInputName.c_str());
			interaction.m_graphInputID = graphInputID;

			char prevValue[256];
			pGraphState->GetInput(graphInputID, prevValue);
			interaction.m_graphPrevValue = prevValue;

			// set the input 
			TAnimationGraphQueryID queryId;
			pGraphState->SetInput(graphInputID, interaction.m_startParams->m_graphInputValue.c_str(), &queryId);
			interaction.m_startParams->m_graphQueryID = queryId;

			// set yourself as listener (but also create a max waiting time)
			pGraphState->AddListener("IKInteractionManager", this);
			interaction.m_timerHelper = 5.0f; // wait xx seconds and then continue regardless of graph
			interaction.m_interactionState = IKState_WaitForAnimation;
		}
	}
}

//////////////////////////////////////////////////////////////////////////

void CIKInteractionManager::SetOffsetForInteraction( int interactionId, QuatT newOffset )
{
	// Check whether the interaction is valid
	EntityId charId;
	SActiveInteraction* activeInteraction = GetInteractionById(interactionId, charId);
	if (!activeInteraction)
	{
		return;
	}

	// Check whether the state is fading out for good
	if (activeInteraction->m_interactionState == IKState_FadingOut)
	{
		return;
	}


	// Calculate the offset for all involved limbs/IK Targets with the new offset
	activeInteraction->m_additionalOffsetPos = newOffset.t;
	activeInteraction->m_additionalOffsetRot = newOffset.q;
	SIKInteractionStartParameters params = CreateStartParamsFromInteraction(charId, *activeInteraction);
	Vec3 helpVec = activeInteraction->m_helpVec;
	CalculateOffsetsForIKHelpers(params, activeInteraction->m_selectedHelpers, helpVec);
	activeInteraction->m_helpVec = helpVec;

	// Update the offset of all the targets in the IKTarget System
	std::vector<uint64>::const_iterator ikIterator = activeInteraction->m_ikHandles.begin();
	for (int i =0; ikIterator != activeInteraction->m_ikHandles.end(); ++ikIterator, ++i)
	{
		CCryAction::GetCryAction()->GetIKTargetSystem()->SetOffsetOnTarget(charId, *ikIterator, activeInteraction->m_selectedHelpers.at(i).m_offset);
	}
		
}

//////////////////////////////////////////////////////////////////////////

CIKInteractionManager::SActiveInteraction* CIKInteractionManager::GetInteractionById( int interactionId, EntityId& characterId )
{
	// go through all m_activeInteractions and find an interaction with that id
	std::map<EntityId, TCharacterInteractions*>::const_iterator itInteractions = m_activeInteractions.begin();
	std::map<EntityId, TCharacterInteractions*>::const_iterator itEndInteractions = m_activeInteractions.end();
	TCharacterInteractions::iterator itCharActions;
	TCharacterInteractions::const_iterator itEndCharActions;
	for ( ; itInteractions != itEndInteractions; ++itInteractions)
	{
		// go through all interactions for this character and check if one of them is the one
		TCharacterInteractions* pCharInteractions = itInteractions->second;
		itCharActions = pCharInteractions->begin();
		itEndCharActions = pCharInteractions->end();
		for ( ; itCharActions != itEndCharActions; ++itCharActions)
		{
			SActiveInteraction* activeInteraction = &(*itCharActions);
			if (activeInteraction->m_interactionId == interactionId)
			{
				characterId = itInteractions->first;
				return activeInteraction;
			}
		}
	}

	return NULL;
}

//////////////////////////////////////////////////////////////////////////

#ifdef _DEBUG

void CIKInteractionManager::SDebugInformation::Render()
{
	gEnv->pRenderer->GetIRenderAuxGeom()->DrawLine(pos1, ColorB(255,0,0), pos1 + dir1, ColorB(255,0,0), 2.0f);
	gEnv->pRenderer->GetIRenderAuxGeom()->DrawLine(pos2, ColorB(0,255,0), pos2 + dir2, ColorB(0,255,0), 2.0f);
	gEnv->pRenderer->GetIRenderAuxGeom()->DrawLine(pos3, ColorB(0,0,255), pos3 + dir3, ColorB(0,0,255), 2.0f);

	gEnv->pRenderer->GetIRenderAuxGeom()->DrawSphere(sphere1, 0.05f, ColorB(255,0,0));
	gEnv->pRenderer->GetIRenderAuxGeom()->DrawSphere(sphere2, 0.05f, ColorB(0,255,0));
	gEnv->pRenderer->GetIRenderAuxGeom()->DrawSphere(sphere3, 0.05f, ColorB(0,0,255));

}

#endif // _DEBUG

